/*
 * File     : X86_64_Unix.s
 * Created  : 21 Apr 2011
 *
 * Copyright © 2011 Matthew Wilson (mj. {my-surname} .uk {at} gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

.intel_syntax
.text


////////////////////////////////////////////////////////////////////////////////
// Call for <=3 int arguments, any number of float arguments                  //
////////////////////////////////////////////////////////////////////////////////

.globl dni_x86_64_unix_call_int_3
    .type dni_x86_64_unix_call_int_3, @function

dni_x86_64_unix_call_int_3:
    // Java JNI signature is:
    //   long      fnptr
    //   long      iarg0
    //   long      iarg1
    //   long      iarg2
    //   double    farg[0..]  // optional
    //
    // Registers:
    //   rdi:      JNIEnv*
    //   rsi:      jclass
    //   rdx:      fnptr
    //   rcx:      iarg0
    //   r8:       iarg1
    //   r9:       iarg2
    //   xmm0..7:  farg0..7
    //
    // Stack:
    //   rsp+8:    (actual spill)

    // preserve fnptr
    movq      %r10, %rdx

    // move displaced arguments
    mov       %rdi, %rcx
    mov       %rsi, %r8
    mov       %rdx, %r9

    jmp       %r10


////////////////////////////////////////////////////////////////////////////////
// Call for 4 int arguments, any number of float arguments                    //
////////////////////////////////////////////////////////////////////////////////

.globl dni_x86_64_unix_call_int_4
    .type dni_x86_64_unix_call_int_4, @function

dni_x86_64_unix_call_int_4:
    // Java JNI signature is:
    //   long      fnptr
    //   int       offset
    //   long      iarg0
    //   long      iarg1
    //   double    farg[0..]  // optional
    //   long      iarg2      // on stack
    //   long      iarg3      // on stack
    //
    // Registers:
    //   rdi:       JNIEnv*
    //   rsi:       jclass
    //   rdx:       fnptr
    //   rcx:       offset
    //   r8:        iarg0
    //   r9:        iarg1
    //   xmm0..7:   farg0..7
    //
    // Stack:
    //   rsp+8:       (actual spill)
    //   rsp+ecx+ 0:  iarg2
    //   rsp+ecx+ 8:  iarg3


    // preserve fnptr
    movq      %r10, %rdx

    // move displaced items
    movq      %rdi, %r8
    movq      %rsi, %r9

    // move forced spill items into registers
    add       %rcx, %rsp
    movq      %rdx, [ %rcx +  0 ]
    movq      %rcx, [ %rcx +  8 ]

    jmp       %r10


////////////////////////////////////////////////////////////////////////////////
// Call for 5 int arguments, any number of float arguments                    //
////////////////////////////////////////////////////////////////////////////////

.globl dni_x86_64_unix_call_int_5
    .type dni_x86_64_unix_call_int_5, @function

dni_x86_64_unix_call_int_5:
    // Java JNI signature is:
    //   long      fnptr
    //   int       offset
    //   long      iarg4
    //   long      iarg0
    //   double    farg[0..]  // optional
    //   long      iarg1      // on stack
    //   long      iarg2      // on stack
    //   long      iarg3      // on stack
    //
    // Registers:
    //   rdi:       JNIEnv*
    //   rsi:       jclass
    //   rdx:       fnptr
    //   rcx:       offset
    //   r8:        iarg4  (ALREADY CORRECT)
    //   r9:        iarg0
    //   xmm0..7:   farg0..7
    //
    // Stack:
    //   rsp+8:       (actual spill)
    //   rsp+ecx+ 0:  iarg1
    //   rsp+ecx+ 8:  iarg2
    //   rsp+ecx+16:  iarg3


    // preserve fnptr
    movq      %r10, %rdx

    // move displaced items
    movq      %rdi, %r9

    // move forced spill items into registers
    add       %rcx, %rsp
    movq      %rsi, [ %rcx +  0 ]
    movq      %rdx, [ %rcx +  8 ]
    movq      %rcx, [ %rcx + 16 ]

    jmp       %r10


////////////////////////////////////////////////////////////////////////////////
// Call for >=6 int arguments, any number of float arguments                  //
////////////////////////////////////////////////////////////////////////////////

.globl dni_x86_64_unix_call_int_6
    .type dni_x86_64_unix_call_int_6, @function

dni_x86_64_unix_call_int_6:
    // Java JNI signature is:
    //   long      fnptr
    //   long      offset
    //   long      iarg4
    //   long      iarg5
    //   double    farg[0..]  // optional
    //   long      iarg[6..]  // optional
    //   long      iarg0      // on stack
    //   long      iarg1      // on stack
    //   long      iarg2      // on stack
    //   long      iarg3      // on stack
    //
    // Registers:
    //   rdi:       JNIEnv*
    //   rsi:       jclass
    //   rdx:       fnptr
    //   rcx:       offset
    //   r8:        iarg4  (ALREADY CORRECT)
    //   r9:        iarg5  (ALREADY CORRECT)
    //   xmm0..7:   farg0..7
    //
    // Stack:
    //   rsp+8:       (actual spill)
    //   rsp+ecx+ 0:  iarg0
    //   rsp+ecx+ 8:  iarg1
    //   rsp+ecx+16:  iarg2
    //   rsp+ecx+24:  iarg3


    // preserve fnptr
    movq      %r10, %rdx

    // move forced spill items into registers
    add       %rcx, %rsp
    movq      %rdi, [ %rcx +  0 ]
    movq      %rsi, [ %rcx +  8 ]
    movq      %rdx, [ %rcx + 16 ]
    movq      %rcx, [ %rcx + 24 ]

    jmp       %r10


////////////////////////////////////////////////////////////////////////////////
// Call for any number of arguments, reading errno                            //
////////////////////////////////////////////////////////////////////////////////

.globl dni_x86_64_unix_call_errno
    .type dni_x86_64_unix_call_errno, @function

dni_x86_64_unix_call_errno:
    // Java JNI signature is:
    //   long      fnptr
    //   long      offset
    //   long      iarg4
    //   long      iarg5
    //   double    farg[0..]  // optional
    //   long      iarg[6..]  // optional
    //   long      iarg0      // on stack
    //   long      iarg1      // on stack
    //   long      iarg2      // on stack
    //   long      iarg3      // on stack
    //   long      lastErrorSlotPtr
    //   long      empty1
    //   long      empty2
    //
    // Registers:
    //   rdi:       JNIEnv*
    //   rsi:       jclass
    //   rdx:       fnptr
    //   rcx:       offset
    //   r8:        iarg4  (ALREADY CORRECT)
    //   r9:        iarg5  (ALREADY CORRECT)
    //   xmm0..7:   farg0..7
    //
    // Stack:
    //   rsp+8:       (actual spill)
    //   rsp+ecx+ 0:  iarg0
    //   rsp+ecx+ 8:  iarg1
    //   rsp+ecx+16:  iarg2
    //   rsp+ecx+24:  iarg3
    //   rsp+ecx:32:  lastErrorSlotPtr
    //   rsp+ecx:40:  empty (to preserve old r12)
    //   rsp+ecx:48:  empty (to preserve return address)


    // preserve old r12
    add       %rcx, %rsp
    movq      %r12, [ %rcx + 40 ]
    
    // remember our magic spill area (r12 has to be preserved by callee)
    mov       %r12, %rcx
    
    // preserve return address
    pop       %rdi
    movq      [ %rcx + 48 ], %rdi
    
    // preserve fnptr
    movq      %r10, %rdx

    // move forced spill items into registers
    movq      %rdi, [ %rcx +  0 ]
    movq      %rsi, [ %rcx +  8 ]
    movq      %rdx, [ %rcx + 16 ]
    movq      %rcx, [ %rcx + 24 ]

    // make a call, returning back here
    call      %r10

    // record return result
    movq      [ %r12 + 0 ], %rax
    movq      [ %r12 + 8 ], %xmm0

    // retrieve the address of errno
    call      __errno_location@PLT
    
    // read and store the value of errno
    movq      %rdi, [ %r12 + 32 ] 
    mov       %ecx, [ %rax ]
    mov       [ %rdi ], %ecx
    
    // retrieve return result
    movq      %rax, [ %r12 + 0 ] 
    movq      %xmm0, [ %r12 + 8 ] 
    
    // restore r12
    movq      [ %r12 + 40 ], %r12
    
    // restore return address
    movq      %rdi, [ %r12 + 48 ] 
    push      %rdi
    
    ret


////////////////////////////////////////////////////////////////////////////////
// Call-back (Native Thread)                                                  //
////////////////////////////////////////////////////////////////////////////////

.globl dni_x86_64_unix_callback
    .type dni_x86_64_unix_callback, @function

dni_x86_64_unix_callback:

    // maintain stack frame
    push      %rbp
    mov       %rbp, %rsp

    // function stack layout:
    // %rbp - 8 *  0 = old %rbp
    //             1 = ptr to Java-info structure
    //             2 = rdi (int param 0)
    //             3 = rsi (int param 1)
    //             4 = rdx (int param 2)
    //             5 = rcx (int param 3)
    //             6 = r8  (int param 4)
    //             7 = r9  (int param 5)
    //             8 = xmm0 (float param 0)
    //             9 = xmm1 (float param 1)
    //            10 = xmm2 (float param 2)
    //            11 = xmm3 (float param 3)
    //            12 = xmm4 (float param 4)
    //            13 = xmm5 (float param 5)
    //            14 = xmm6 (float param 6); return value, upper 64-bits
    //            15 = xmm7 (float param 7); return value, lower 64-bits
    //            16 = JNIEnv* env

    // the hidden argument is in r11 - this is the pointer to the structure of
    // Java data required:
    //   [ %r11 +  0 ] = JavaVM*   javaVM
    //   [ %r11 +  8 ] = jclass    clazz     // callback's class
    //   [ %r11 + 16 ] = jmethodID method    // static helper method
    //   [ %r11 + 24 ] = jobject   callback
    push      %r11

    // Note:  Stack MUST be aligned to a multiple of 16 bytes (2 qwords) when
    // making any calls.  If an odd number of qwords will be pushed, the return
    // address pushed automatically by 'call' will make it an even number.

    // push all registers that might have arguments in them
    sub       %rsp, 64
    movq      [ %rsp +  0 ], %xmm0
    movq      [ %rsp +  8 ], %xmm1
    movq      [ %rsp + 16 ], %xmm2
    movq      [ %rsp + 24 ], %xmm3
    movq      [ %rsp + 32 ], %xmm4
    movq      [ %rsp + 40 ], %xmm5
    movq      [ %rsp + 48 ], %xmm6
    movq      [ %rsp + 56 ], %xmm7
    push      %r9
    push      %r8
    push      %rcx
    push      %rdx
    push      %rsi
    push      %rdi

    // allocate space for locals
    sub       %rsp, 8

    // (*javaVM)->AttachCurrentThreadAsDaemon( javaVM, &env, NULL )
    // (index 7 in the JavaVM function table)
    mov       %rdi, [ %r11 ]
    lea       %rsi, [ %rbp + 16 * 8 ]
    mov       %rdx, 0
    mov       %r8,  [ %rdi ]
    call      [ %r8 + 7 * 8 ]

    // not much we can do if AttachCurrentThreadAsDaemon fails
    cmp       %eax, 0
    jne       dni_x86_64_unix_callback_return

    // (*env)->CallStaticVoidMethod( env, class, methodID, callback, %rbp )
    // (index 143 in the JNIEnv function table)
    mov       %rdi, [ %rbp - 16 * 8 ]
    mov       %rcx, [ %rbp -  1 * 8 ]
    mov       %rsi, [ %rcx + 8 ]
    mov       %rdx, [ %rcx + 16 ]
    mov       %rcx, [ %rcx + 24 ]
    mov       %r8,  %rbp
    mov       %r9,  [ %rdi ]
    xor       %rax, %rax
    call      [ %r9 + 143 * 8 ]

    // return the integer / FP value
    mov       %rax,  [ %rbp - 15 * 8 ]
    mov       %rcx,  [ %rbp - 14 * 8 ]
    movq      %xmm0, [ %rbp - 15 * 8 ]

dni_x86_64_unix_callback_return:
    // restore base pointer
    mov       %rsp, %rbp
    pop       %rbp

    ret
